
#ifndef CONFIG_H_
#define CONFIG_H_

#include <fstream>
#include <sstream>
#include <iostream>
#include <list>

#include <assert.h>

#include "esfera.h"
#include "cubo.h"

#include "iexcepciones.h"
#include "separador.h"

/* Encapsula la obtencion de los parametros que configuran la
 * vista 3d como la resolucion, o la cantidad de planos 2d a utilizar
 * para construir la vista 3d.
 * Ademas se encarga de obtener los datos necesarios para recontruir
 * las esferas. 
 * Dicha reconstruccion 3d no es realizada en esta clase y es delegada
 * a otras.
 */
class Config {
   private:
      Config(const Config &aCopiar);
      const Config& operator=(const Config &aCopiar);

      bool inicializado;

      const std::ios::iostate cualquierError;
      bool leyendoEsfera;

      unsigned int _resolucion;
      unsigned int _cantPlanos;

      /* Carga la cabecera del archivo de configuracion, 
       * esto es: la resolucion y la cantidad de planos.
       * */
      void cargarCabecera(std::ifstream &arch) {
         arch >> _resolucion >> sep >> _cantPlanos >> sep ;
      }

      /* Carga los datos para construir una esfera.
       * Requiere:
       *    - radio de la esfera
       *    - posicion x y z
       *    - color r g b
       * Todos los valores deben estar separados por al caracter blanco
       * (espacio, tab, salto  de linea, etc.)
       * */
      void cargarEsfera(std::ifstream &arch, CuboDeEsferas &esferas) {
         float radio;
         float posicion[3];
         float color[3];

         arch >> radio >> sep;
         leyendoEsfera = true;

         arch >> posicion[0] >> posicion[1] >> posicion[2] >> sep;

         arch >> color[0] >> color[1] >> color[2] >> sep;

         leyendoEsfera = false;

         esferas.agregar(Esfera(radio, posicion, color));
      }


   public:

      Config() : inicializado(false),
                  cualquierError(std::ios::badbit | std::ios::failbit | std::ios::eofbit),
                  leyendoEsfera(false)
                  {}
      
      inline unsigned int resolucion() const {
         assert(inicializado);
         return _resolucion;
      }

      inline unsigned int cantPlanos() const {
         assert(inicializado);
         return _cantPlanos;
      }

      /* Carga la configuracion desde el archivo cuyo nombre es pasado
       * por parametro.
       * Ademas agrega las esferas leidas al Cubo de esferas.
       * Cualquier error producido lanzara una excepcion.
       *
       * */
      void fromArchivo(const char* nombre, CuboDeEsferas &esferas){
         assert(nombre != 0);
         inicializado = false;

         std::ifstream arch(nombre);

         if(not arch.is_open()) 
            throw NoExisteElArchivo();

         using namespace std;
         arch.exceptions(cualquierError);

         try {
            cargarCabecera(arch);
         } catch (const ios::failure &error){
            throw LecturaCabeceraException();
         }

         unsigned int numEsfera = 1;
         try {
            while(true) {
               cargarEsfera(arch, esferas);
               numEsfera++;
            }
         } catch(const ios::failure &error) {
            if(not arch.eof() or leyendoEsfera) {
               throw LecturaEsferaException(arch.eof(), numEsfera);
            }
         }
         
         inicializado = true;
      }
};

#endif
